/*
 * Program to measure TSC register jitter between two CPUs
 *
 * Copyright (C) 2006 Nikolai Joukov and Erez Zadok
 * Copyright (C) 2006 Stony Brook University
 */

#include <sched.h>
#include <stdio.h>
#include <string.h>

#define likely(x)	__builtin_expect(!!(x), 1)
#define unlikely(x)	__builtin_expect(!!(x), 0)

#define RDTSC(qp) \
do { \
        unsigned long lowPart, highPart; \
                __asm__ __volatile__("rdtsc" : "=a" (lowPart), "=d" (highPart)); \
                qp = (((unsigned long long) highPart) << 32) | lowPart; \
} while (0);
 
//#define AFFINITY

int TSC_reader(void *arg)
{
#ifdef AFFINITY
	int cpumask = 2;
#endif
	long long * ll = (long long*)arg;

#ifdef AFFINITY	
	if (sched_setaffinity(getpid(), 2, &cpumask) != 0)
                printf("child setaffinity failed\n");
#endif

	while (ll[1])
		RDTSC(ll[0]);

	return 0;
}

#define STACK_SIZE 512
#define BUCKETS_NUM (sizeof(long long)*8)

int main(int argc, char* argv[])
{
	void	 *stack[STACK_SIZE];
	int	  err = 0;
	unsigned long long ll, delay, TSC[2];
	unsigned long long aggregate_stats[BUCKETS_NUM];
	int bucket, idelay, it;
#ifdef AFFINITY
	int cpumask = 1;
#endif

	TSC[0] = 0;
	TSC[1] = 1;

	if (argc != 2) {
		printf("usage: %s iterations\n", argv[0]);
		return 2;
	}
	it = atoi(argv[1]);

	err = clone(TSC_reader, stack + STACK_SIZE, CLONE_VM, (void*)TSC);
	if (!err) {
		perror("clone:");
		return 2;
	}

	memset(aggregate_stats, 0, sizeof(aggregate_stats));

#ifdef AFFINITY
	if (sched_setaffinity(getpid(), 2, &cpumask) != 0)
                printf("parent setaffinity failed\n");
#endif

	/* wait for the TSC reader to start */
	while (TSC[0] == 0) {};
		/* sched_yield();*/

	/* slow but it is ok here */
	for (; it; it--) {
		RDTSC(ll);
		if (ll > TSC[0])
			delay = ll - TSC[0];
		else
			delay = TSC[0] - ll;
		delay = delay >> 5;
		idelay = delay >> 32;
		if (idelay) {
			bucket = 31;
		} else {
			bucket = 0;
			idelay = (unsigned int)delay;
		}
		for (; idelay && (bucket + 1) < BUCKETS_NUM; bucket++) {
			idelay = idelay >> 1;
		}
		aggregate_stats[bucket]++;
	}

	TSC[1] = 0;
	printf("OP_TSC_JITTER ? ? ");
        for (bucket = 0; bucket < BUCKETS_NUM; bucket++)
		printf("%lld ", aggregate_stats[bucket]);
	printf("\n");

	return 1;
}

